<template>
    <div class="wh-100% flex gap-2.5">
        <div ref="chartRef" class="w-40% h-full"></div>
        <n-card class="w-60% overflow-hidden" content-class="flex">
            <template #header>
                <div class="w-full flex justify-between">
                    <div class="font-size-4">
                        {{ currentDistrict }} 未来用电量
                    </div>
                    <n-tabs
                        size="small"
                        type="segment"
                        class="w-60"
                        @update:value="handleChangeTab"
                    >
                        <n-tab name="chart" label="图表" />
                        <n-tab name="table" label="表格" />
                    </n-tabs>
                </div>
            </template>
            <div
                v-if="currentElectricityData.length === 0"
                class="flex-center wh-100%"
            >
                <n-result
                    status="418"
                    title="暂无数据"
                    description="一切尽在不言中"
                >
                </n-result>
            </div>
            <div v-else class="flex-1 overflow-scroll">
                <div
                    v-show="activeTab === 'chart'"
                    ref="barChartRef"
                    class="wh-100%"
                ></div>
                <n-data-table
                    v-show="activeTab === 'table'"
                    :columns="electricityPredictionColumns"
                    :data="electricityData.get(props.timestamp)"
                    :scroll-x="1500"
                    max-height="60vh"
                />
            </div>
        </n-card>
    </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, watch, nextTick } from "vue";

import * as echarts from "echarts";
import GZGeoJson from "./GZ_geoJson";
import { electricityPredictionColumns } from "@/utils/table-column";

import { ElectricityPrediction } from "@/types/prediction";
import { getElectricityPrediction } from "@/service/api/predict";

interface ElectricityData {
    data: number;
    date: string;
}

const props = defineProps<{
    timestamp: number;
}>();

const chartRef = ref<HTMLElement | null>(null);
let chartInstance: echarts.ECharts | null = null;

const barChartRef = ref<HTMLElement | null>(null);
let barChartInstance: echarts.ECharts | null = null;

const electricityData = new Map<number, ElectricityPrediction[]>();
const currentElectricityData = ref<ElectricityData[]>([]);

const currentDistrict = ref<string>("全城各区");
const activeTab = ref<"chart" | "table">("chart");

const guangzhouDistricts: { [key: string]: string } = {
    越秀区: "yuexiu",
    荔湾区: "liwan",
    海珠区: "haizhu",
    天河区: "tianhe",
    白云区: "baiyun",
    黄埔区: "huangpu",
    番禺区: "panyu",
    花都区: "huadu",
    南沙区: "nansha",
    从化区: "conghua",
    增城区: "zengcheng",
};

const updateChart = () => {
    if (chartInstance) {
        chartInstance.dispose();
        chartInstance = null;
    }

    chartInstance = echarts.init(chartRef.value);

    echarts.registerMap("Guangzhou", GZGeoJson);

    const option = {
        tooltip: {
            trigger: "item",
            formatter: "{b}",
        },
        series: [
            {
                type: "map",
                map: "Guangzhou",
                roam: true,
                label: {
                    show: true,
                },
            },
        ],
    };
    chartInstance.setOption(option);

    chartInstance.on("click", (params) => {
        if (activeTab.value === "table") return;

        currentDistrict.value = params.name;

        // 点击地图更新图表
        getElectricityData(currentDistrict.value);
        updateBarChart();
    });

    chartInstance.getZr().on("click", (param) => {
        if (activeTab.value === "table") return;

        const pointInPixel = [param.offsetX, param.offsetY];
        // 判断点击位置是否在图表元素上
        if (chartInstance?.containPixel("series", pointInPixel)) {
            return;
        }

        // 取消选中高亮
        chartInstance?.dispatchAction({
            type: "unselect",
            name: currentDistrict.value,
        });

        // 点击地图更新图表
        currentDistrict.value = "全城各区";
        getElectricityData(currentDistrict.value);
        updateBarChart();
    });
};

const updateBarChart = () => {
    if (barChartInstance) {
        barChartInstance.dispose();
        barChartInstance = null;
    }

    if (barChartRef.value) {
        barChartInstance = echarts.init(barChartRef.value);
    }

    const data = currentElectricityData.value;

    const option = {
        tooltip: {
            trigger: "axis",
            axisPointer: {
                type: "shadow",
            },
            formatter: (params: any) => {
                return `日期: ${params[0].name} </br> 用电量: ${Math.ceil(
                    params[0].data
                )} 度`;
            },
        },
        xAxis: {
            type: "category",
            name: "日期",
            data: data.map((item) => item.date),
        },
        yAxis: {
            type: "value",
            name: "用电量(度)",
        },
        series: [
            {
                data: data.map((item) => item.data),
                type: "bar",
            },
        ],
    };
    barChartInstance?.clear();
    barChartInstance?.setOption(option);
};

// 窗口大小变化重新绘制图表
const handleResize = () => {
    if (chartInstance) {
        chartInstance.resize();
    }
    if (barChartInstance) {
        barChartInstance.resize();
    }
};

// 从electricityData中获取并格式化需要展示的数据
const getElectricityData = (districtName: string) => {
    const target = electricityData.get(props.timestamp);
    if (!target) {
        return;
    }

    let datas: ElectricityData[] = [];
    if (districtName === "全城各区") {
        // 全城各区的用电量是所有区县的用电量之和
        datas = target.map((item) => ({
            date: item.date,
            data: Object.entries(item)
                .filter(
                    ([key, value]) => key !== "date" && !isNaN(Number(value))
                )
                .reduce((acc, [_, value]) => acc + Math.abs(Number(value)), 0),
        }));
    } else {
        datas = target.map((item) => ({
            date: item.date,
            data: Number(
                item[
                    guangzhouDistricts[
                        districtName
                    ] as keyof ElectricityPrediction
                ]
            ),
        }));
    }
    currentElectricityData.value = datas;
};

const handleQuery = async () => {
    try {
        const res = await getElectricityPrediction(props.timestamp);
        electricityData.set(
            props.timestamp,
            res.data as ElectricityPrediction[]
        );
    } catch (err) {
        electricityData.set(props.timestamp, []);
        window.NMessage.error((err as Error).message);
    }
};

watch(
    () => props.timestamp,
    async () => {
        if (electricityData.has(props.timestamp)) {
            getElectricityData(currentDistrict.value);
        } else {
            await handleQuery();
            getElectricityData(currentDistrict.value);
        }
        nextTick(() => {
            updateBarChart();
        });
    }
);

onMounted(async () => {
    try {
        await handleQuery();

        getElectricityData(currentDistrict.value);

        nextTick(() => {
            updateChart();
            updateBarChart();
        });

        window.addEventListener("resize", handleResize);
    } catch (err) {}
});

// 切换tab事件
const handleChangeTab = (value: "chart" | "table") => {
    // 取消选中高亮
    chartInstance?.dispatchAction({
        type: "unselect",
        name: currentDistrict.value,
    });

    currentDistrict.value = "全城各区";
    activeTab.value = value;

    if (value === "chart") {
        nextTick(() => {
            // 将图表数据切换回全城
            getElectricityData(currentDistrict.value);
            updateBarChart();
        });
    }
};

const getCurrentData = () => {
    if (currentElectricityData.value.length === 0) {
        return null;
    }
    return currentElectricityData.value;
};

const handleReload = async () => {
    await handleQuery();
    nextTick(() => {
        updateBarChart();
    });
};

defineExpose({
    getCurrentData,
    handleReload,
});
</script>

<style scoped></style>
